import { $set } from '@feng3d/serialization';
import { Color, IcosahedronGeometry3D, InstancedMesh3D, Matrix4, MeshPhongMaterial3D, Node3D, PerspectiveCamera3D, Raycaster3D, Scene3D, StatsComponent, Vector2, WebGLRenderer3D } from 'feng3d-three';
import GUI from 'lil-gui';
import '../../extension/controls/OrbitControls3D';
import { OrbitControls3D } from '../../extension/controls/OrbitControls3D';

let camera: PerspectiveCamera3D;
let scene: Scene3D;
let renderer: WebGLRenderer3D;
let controls: OrbitControls3D;
let stats: StatsComponent;

let mesh: InstancedMesh3D;
const amount = parseInt(window.location.search.slice(1)) || 10;
const count = Math.pow(amount, 3);

const raycaster = new Raycaster3D();
const mouse = new Vector2(1, 1);

const color = new Color();
const white = new Color().setHex(0xffffff);

init();
animate();

function init()
{
    camera = new Node3D().addComponent('PerspectiveCamera3D', { fov: 60, aspect: window.innerWidth / window.innerHeight, near: 0.1, far: 100 });
    camera.entity.position.set(amount, amount, amount);
    camera.entity.lookAt(0, 0, 0);

    scene = new Node3D().addComponent('Scene3D');

    const light = new Node3D().addComponent('HemisphereLight3D', { color: { value: 0xffffff }, groundColor: { value: 0x888888 } });
    light.entity.position.set(0, 1, 0);
    scene.entity.addChild(light.entity);

    const geometry = $set(new IcosahedronGeometry3D(), { radius: 0.5, detail: 3 });
    const material = $set(new MeshPhongMaterial3D(), { color: { value: 0xffffff } });

    mesh = new Node3D().addComponent('InstancedMesh3D', { geometry, material, count });

    let i = 0;
    const offset = (amount - 1) / 2;

    const matrix = new Matrix4();

    for (let x = 0; x < amount; x++)
    {
        for (let y = 0; y < amount; y++)
        {
            for (let z = 0; z < amount; z++)
            {
                matrix.setPosition(offset - x, offset - y, offset - z);

                mesh.setMatrixAt(i, matrix);
                mesh.setColorAt(i, color);

                i++;
            }
        }
    }

    scene.entity.addChild(mesh.entity);

    //

    const gui = new GUI();
    gui.add(mesh, 'count', 0, count);

    renderer = scene.addComponent('WebGLRenderer3D', { parameters: { antialias: true } });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);

    controls = new Node3D().addComponent('OrbitControls3D', { camera, domElement: renderer.domElement });
    controls.enableDamping = true;
    controls.enableZoom = false;
    controls.enablePan = false;

    stats = scene.addComponent('StatsComponent');
    document.body.appendChild(stats.dom);

    window.addEventListener('resize', onWindowResize);
    document.addEventListener('mousemove', onMouseMove);
}

function onWindowResize()
{
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();

    renderer.setSize(window.innerWidth, window.innerHeight);
}

function onMouseMove(event: MouseEvent)
{
    event.preventDefault();

    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}

function animate()
{
    requestAnimationFrame(animate);

    controls.update();

    raycaster.setFromCamera(mouse, camera);

    const intersection = raycaster.intersectObject(mesh.entity);

    if (intersection.length > 0)
    {
        const instanceId = intersection[0].instanceId;

        mesh.getColorAt(instanceId, color);

        if (color.equals(white))
        {
            mesh.setColorAt(instanceId, color.setHex(Math.random() * 0xffffff));

            mesh.instanceColor.needsUpdate = true;
        }
    }

    render();

    stats.update();
}

function render()
{
    renderer.render(scene, camera);
}
